package com.wstuo.common.exchange;

import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;

import microsoft.exchange.webservices.data.AttachmentCollection;
import microsoft.exchange.webservices.data.BodyType;
import microsoft.exchange.webservices.data.EmailAddress;
import microsoft.exchange.webservices.data.EmailAddressCollection;
import microsoft.exchange.webservices.data.EmailMessage;
import microsoft.exchange.webservices.data.ExchangeCredentials;
import microsoft.exchange.webservices.data.ExchangeService;
import microsoft.exchange.webservices.data.ExchangeVersion;
import microsoft.exchange.webservices.data.FileAttachment;
import microsoft.exchange.webservices.data.FindItemsResults;
import microsoft.exchange.webservices.data.Folder;
import microsoft.exchange.webservices.data.Item;
import microsoft.exchange.webservices.data.ItemSchema;
import microsoft.exchange.webservices.data.ItemView;
import microsoft.exchange.webservices.data.MessageBody;
import microsoft.exchange.webservices.data.PropertySet;
import microsoft.exchange.webservices.data.SearchFilter;
import microsoft.exchange.webservices.data.ServiceLocalException;
import microsoft.exchange.webservices.data.SortDirection;
import microsoft.exchange.webservices.data.WebCredentials;
import microsoft.exchange.webservices.data.WellKnownFolderName;

import com.wstuo.common.config.attachment.dao.IAttachmentDAO;
import com.wstuo.common.config.attachment.entity.Attachment;
import com.wstuo.common.tools.dao.IEmailDAO;
import com.wstuo.common.tools.dto.EmailDTO;
import com.wstuo.common.tools.dto.EmailMessageDTO;
import com.wstuo.common.tools.dto.EmailServerDTO;
import com.wstuo.common.tools.service.IEmailServerService;
import com.wstuo.common.tools.service.IEmailService;
import com.wstuo.common.security.utils.AppConfigUtils;
import com.wstuo.common.util.StringUtils;
import com.wstuo.common.util.TimeUtils;
/**
 * <p>Exchange的EWSjava(Exchange WebService(EWS))实现方式，</p>
 * 因为其他地方已经引用IWebDavService,所以暂时不修改接口名称；
 * @author gs60
 * @see <a href="https://github.com/OfficeDev/ews-java-api"> EWSJava项目主页</a>
 */
public class EWSService implements IWebDavService {
	
	@Autowired
	private IEmailServerService emailServerService;
	@Autowired
	private IEmailService emailService;
	@Autowired
	private IEmailDAO emailDAO;
	@Autowired
	private IAttachmentDAO attachmentDAO;
	
	private static final Logger LOGGER = Logger.getLogger(EWSService.class);
	private static final String ATTACHMENT_PATH = AppConfigUtils.getInstance().getAttachmentPath();
	public final static String UNKNOWN_FROMUSER = "unknown";
	
	/**
	 * Exchange邮件测试方法
	 */
	@Override
	public boolean emailConnTest(EmailServerDTO dto) {
		if (dto != null && StringUtils.hasText(dto.getExchangeEmailAccount())) {
			try {
				ExchangeService service = createExchangeService( dto );
				//WellKnownFolderName.如果是Inbox代表收件箱
		        Folder outbox = Folder.bind(service, WellKnownFolderName.Outbox);
		        Folder inbox = Folder.bind(service, WellKnownFolderName.Inbox);
		        if (inbox != null && outbox != null
		        		&& inbox.getId() != null 
		        		&& outbox.getId() != null ) {
					return true;
				}
			} catch (Exception e) {
				LOGGER.error(e.getMessage() + "\nEXCHANGE_CONNECTION_ERROR:"+ dto );
			}
		}
		return false;
	}
	/**
	 * 读取Exchange收件箱的信息
	 */
	@Override
	public List<EmailMessageDTO> getInboxMail() {
		List<EmailMessageDTO> list = new ArrayList<EmailMessageDTO>();
		try{
			EmailServerDTO dto = emailServerService.findReceiveEmail();
			//读取收件箱的记录
			list.addAll( scanEmail(dto, "INBOX",WellKnownFolderName.Inbox));
		} catch ( Exception e) {
			LOGGER.error(e);
		}
		return list;
	}
	
	
	/**
	 * 扫描Exchange邮件，并保存
	 * @param dto
	 * @param folderName
	 * @param inbox
	 * @return
	 * @throws Exception
	 */
	private List<EmailMessageDTO> scanEmail(EmailServerDTO dto, String folderName,
			WellKnownFolderName inbox) throws Exception{
		
		List<EmailMessageDTO> list = new ArrayList<EmailMessageDTO>();
		com.wstuo.common.tools.entity.EmailMessage emailMessage = 
				emailDAO.findLastestReceiveDate(dto.getExchangeEmailAccount());
		ExchangeService service = createExchangeService( dto );
        ItemView view = new ItemView(Integer.MAX_VALUE);//Integer.MAX_VALUE 10
        view.getOrderBy().add(ItemSchema.DateTimeReceived, SortDirection.Ascending);
        FindItemsResults<Item> findResults = null;
        if (emailMessage != null && emailMessage.getReceiveDate() != null) {
        	Date receive = getTimeDelay( emailMessage.getReceiveDate() );
        	findResults = service.findItems(inbox, new SearchFilter.IsGreaterThan(
        			ItemSchema.DateTimeReceived,receive
        	),view);
		}else{
			findResults = service.findItems(inbox, view);
		}
		String fileFolder = TimeUtils.format(new Date(),IEmailService.FILENAME_PATTERN);
		String saveDir = ATTACHMENT_PATH + fileFolder;
        for ( Item item : findResults.getItems() ) {
            EmailMessage message = EmailMessage.bind(service, item.getId());
            EmailMessageDTO messageDTO = emailMessage2EmailMessageDTO(message);
            messageDTO.setFolderName( folderName );
			//if (message.getHasAttachments()) {////是否存在附件（因为存在内联附件时，不会显示在这个返回值里面）
				messageDTO.setAffachmentPath(saveDir);
				messageDTO.setAffachment( emailAttachment(message,fileFolder) );
				emailContentInlineAttachment(messageDTO);
			//}
            long emailId = saveExchangeEmail(messageDTO);
            if ( emailId != 0) {
            	messageDTO.setEmailMessageId(emailId);
                list.add( messageDTO );
			}
        }
        return list;
	}
	
	/**
	 * 邮件的内联附件的处理
	 * @param dto
	 */
	private void emailContentInlineAttachment(EmailMessageDTO dto){
		String content = null;
		if ( dto != null && StringUtils.hasText(content = dto.getContent()) 
				&& dto.getAffachment() != null && dto.getAffachment().size() > 0) {
			List<Attachment> attas = dto.getAffachment();
			for (Attachment atta : attas) {
				if (atta.getAttachmentName()!= null && atta.getAid() != null) {
					content = replaceImageSrc(content, atta.getAttachmentName(), atta.getAid());
				}
			}
			dto.setContent(content);
		}
	}
	private static final String IMAGE_SRC_CID = "src=\"cid:(";
	private static final String IMAGE_SRC_CID_END = ")[@\\w.]+";
	private static final String ATTACHMENT_DOWNLOAD_URL = "src=\"attachment!download.action?downloadAttachmentId=";
	private String replaceImageSrc(String content,String attaName,Long aid){
		String reg = IMAGE_SRC_CID + attaName + IMAGE_SRC_CID_END;
		Pattern pattern = Pattern.compile(reg);//
		Matcher matcher = pattern.matcher(content);
        while (matcher.find()) {//匹配结果如：src="cid:image001.jpg@01D15ABB.E4003CA0
            String str = matcher.group(0);
            //替换成 src=\"attachment!....AttachmentId=66
            content = content.replace(str, ATTACHMENT_DOWNLOAD_URL + aid);
        }
        return content;
	}
	
	/**
	 * 发送邮件
	 */
	@Override
	public boolean sendEmail(EmailDTO dto) {
		if (dto != null && dto.getTo() != null ) {
			EmailServerDTO emailServerDTO = emailServerService.findEmail();
			try{
				ExchangeService service = createExchangeService( emailServerDTO );
				EmailMessage msg = new EmailMessage(service);//创建邮件
				msg.setSubject(dto.getSubject());// 设置主题
				// 邮件内容
				MessageBody body = MessageBody.getMessageBodyFromText( dto.getContent() );
				body.setBodyType(BodyType.HTML);
				msg.setBody(body);
				for (String to : dto.getTo()) {// to 收件人
					msg.getToRecipients().add( to );
				}
				if (dto.getCc() != null) {//抄送
					String[] ccs = dto.getCc().split(";");
					for (String cc : ccs) {
						msg.getCcRecipients().add(cc);
					}
				}
				String[] attachments = dto.getAffachmentPath();// 附件
				File dir = attachmentPath();
				dto.setMailPath(dir.getAbsolutePath());
				if( attachments != null && attachments.length > 0){
					for (String atta : attachments) {
						msg.getAttachments().addFileAttachment(atta);
					}
				}
				msg.send();
				return true;
			} catch (Exception e) {
				LOGGER.error(e);
			}finally{
				dto.setFrom( getFrom( new EmailAddress( emailServerDTO.getExchangeUserName()
						,emailServerDTO.getExchangeEmailAccount()) ) );
				saveExchangeEmail(dto);//保存邮件
			}
		}
		return false;
	}
	/**
	 * 创建service连接，并选择服务器使用的Exchange版本
	 * @param dto
	 * @return
	 * @throws URISyntaxException
	 */
	private ExchangeService createExchangeService(EmailServerDTO dto) throws URISyntaxException{
		ExchangeService service = new ExchangeService(getExchangeVersion(dto.getEmailVersion()));
		ExchangeCredentials credentials = new WebCredentials( dto.getExchangeUserName() ,
				dto.getExchangePassword(),dto.getDomain() );// 用户名、密码 
		service.setCredentials(credentials);
		service.setUrl(new URI( dto.getExchangeHostName() +"/ews/exchange.asmx"));// 设置协议地址
		return service;
	}
	
	/**
	 * 扫描结果转换
	 * @param message
	 * @param serverDTO
	 * @return
	 */
	private EmailMessageDTO emailMessage2EmailMessageDTO(EmailMessage message){
		EmailMessageDTO dto = new EmailMessageDTO();
		try {
			EmailAddress emailAddr = message.getFrom();
			dto.setSubject( message.getSubject() );
			dto.setContent( message.getBody().toString() );
			dto.setFromUser( getFrom(emailAddr) );
			dto.setKeyword( message.getId().getUniqueId( ));
			dto.setToUser( receiveAddressToString(
					emailAddress(message.getToRecipients()))
				);
			//dto.setReceiveDate( getTimeDifference( message.getDateTimeReceived() , true) );
			dto.setReceiveDate( message.getDateTimeReceived() );
		} catch (ServiceLocalException e) {
			LOGGER.error(e);
		}
		return dto;
	}
	
	/**
	 * Exchange地址提取转换
	 * @param address
	 * @return
	 */
	private List<String> emailAddress(EmailAddressCollection address){
		List<String> tos = new ArrayList<String>();
		for (EmailAddress addr : address) {
			tos.add( getFrom( addr ));		
		}
		return tos;
	}
	/**
	 * 发送结果保存
	 * @param dto
	 * @return
	 */
	private Long saveExchangeEmail(EmailDTO dto){
		com.wstuo.common.tools.entity.EmailMessage emailMessage = 
				new com.wstuo.common.tools.entity.EmailMessage();
		BeanUtils.copyProperties(dto, emailMessage);
		emailMessage.setFolderName("OUTBOX");//发送的标识
		emailMessage.setSendDate( new Date());// 发送时间
		emailMessage.setFromUser( dto.getFrom() );// 发件人
		emailMessage.setDescription( dto.getFrom() );//具体的发件账户
		emailMessage.setToUser(receiveAddressToString(dto.getTo()));// 接收人
		return emailService.saveEmailMessages(emailMessage);// 记录保存到数据
	}
	/**
	 * 扫描结果保存
	 * @param dto
	 * @return
	 */
	private Long saveExchangeEmail(EmailMessageDTO dto){
		com.wstuo.common.tools.entity.EmailMessage emailMessage = 
				new com.wstuo.common.tools.entity.EmailMessage();
		BeanUtils.copyProperties(dto, emailMessage);
		emailMessage.setFolderName( dto.getFolderName() );
		//emailMessage.setSendDate( new Date());// 发送时间
		emailMessage.setFromUser( dto.getFromUser() );// 发件人
		emailMessage.setDescription( dto.getDescription() );//具体的发件账户
		emailMessage.setToUser( dto.getToUser() );// 接收人
		emailMessage.setReceiveDate( dto.getReceiveDate() );
		if (dto.getAffachment() != null && dto.getAffachment().size() > 0) {////是否存在附件
			emailMessage.setAffachmentPath( dto.getAffachmentPath() );
			emailMessage.setAttachment( dto.getAffachment() );
		}
		return emailService.saveEmailMessages(emailMessage);// 记录保存到数据
	}
	/**
	 * 接收地址List转String
	 * @param tos
	 * @return String
	 */
	private String receiveAddressToString(List<String> tos) {
		StringBuffer sb = new StringBuffer("");
		if (tos != null) {
			for (String to : tos) {
				if (StringUtils.hasText(sb.toString())){
					sb.append(";" + to);
				}else{
					sb.append( to );
				}
			}
		}
		return sb.toString();

	}
	/**
	 * 获得发件人的地址和姓名
	 * 
	 * @param message
	 * @return String
	 * @exception Exception
	 */
	private String getFrom(EmailAddress emailAddress) {
		String personal = emailAddress.getName();
		String from = emailAddress.getAddress();
		if (personal == null) {
			personal = "";
		}
		if (from == null) {
			from = "";
		}
		return  personal + "<" + from + ">";
	}
	/**
	 * 保存附件
	 * @param message
	 * @param fileFolder
	 * @return
	 */
	private List<Attachment> emailAttachment(EmailMessage message,String fileFolder){
		List<Attachment> attachments = new ArrayList<Attachment>();
		try {
			message.load(PropertySet.FirstClassProperties);
			AttachmentCollection attas = message.getAttachments();
			for (microsoft.exchange.webservices.data.Attachment atta : attas) {
				String attaName = atta.getName();
				try {
					if ( !atta.getContentType().contains( "rfc822" ) //电子邮件的标准格式 (RFC 822)
							) {
						FileAttachment attachment = (FileAttachment) atta;
						String fileName = new Date().getTime() + getFileExtension(attaName);
						Attachment myAtta = new Attachment();
						myAtta.setAttachmentName( attaName );
						myAtta.setUrl(fileFolder +File.separator +fileName);
						File dir = attachmentPath();
						File f = new File(dir,fileName);
						f.createNewFile();
						attachment.load( attaName = f.getAbsolutePath() );
						myAtta.setType("itsm.request");
						attachments.add(myAtta);
						attachmentDAO.save(myAtta);
					}
				} catch (ClassCastException e) {
					LOGGER.error(e.getMessage() + "\nERROR_ATTACHMENT_TYPE:"+atta.getContentType());
				} catch (Exception e) {
					LOGGER.error(e.getMessage() + "\nFILE_CREATE_ERROR:"+attaName);
				}
			}
		} catch (Exception e) {
			LOGGER.error(e);
		}
		return attachments;
	}
	/**
	 * 附件保存目录生成
	 * @return
	 */
	private File attachmentPath(){
		String fileFolder = TimeUtils.format(new Date(),IEmailService.FILENAME_PATTERN);
		String saveDir = ATTACHMENT_PATH + fileFolder;
		File dir = new File(saveDir);
		if (!dir.exists()) {
			dir.mkdirs();
		}
		return dir;
	}
	
	/**
	 * 获取文件扩展名
	 * @param file
	 * @return
	 */
	private static String getFileExtension(String fileName) {
		if (fileName.lastIndexOf(".") != -1 && fileName.lastIndexOf(".") != 0) {
			return fileName.substring(fileName.lastIndexOf(".") - 1 + 1);
		} else {
			return "";
		}
	}
	/**通过字符串生成ExchangeVersion枚举值
	 * <String "Exchange2007_SP1">
	 * <String "Exchange2010">
	 * <String "Exchange2010_SP1">
	 * <String "Exchange2010_SP2">
	 * @param version
	 * @return
	 */
	private ExchangeVersion getExchangeVersion(String version){
		ExchangeVersion exchangeVersion = ExchangeVersion.Exchange2007_SP1;
		if (version != null && version.length() > 1) {
			try {
				exchangeVersion = ExchangeVersion.valueOf(version);
			} catch (Exception e) {
				LOGGER.error(e.getMessage() + "\nNUNKNOWN_EXCHANGE_VERSION:"+version);
			}
		}
		return exchangeVersion;
	}
	/**
	 * 对传入日期进行延时1秒；
	 * @param date
	 * @return
	 */
	private Date getTimeDelay(Date date){
		Calendar calendar = Calendar.getInstance();
		if (date != null) {
			calendar.setTime(date);
			calendar.add(Calendar.SECOND, 1);
		}
		return calendar.getTime();
	}
}
